#include "evenodd.h"

void xor_block(char *dst, char *src) {
    int i;
    for (i = 0; i < BLKSZ; i++) {
        dst[i] ^= src[i];
    }
}

void check_all_disk(int n) {
    int i;
    char dir_path[32] = "./metadisk";
    // printf("check_all_disk %d\n", n);
    if (access(dir_path, F_OK)) {
        mkdir(dir_path, 0777);
    }
    for (i = 0; i < n; i++) {
        snprintf(dir_path, 32, "./disk_%d", i);
        if (access(dir_path, F_OK)) {
            mkdir(dir_path, 0777);
        }
    }
}

void thread_create_and_join(off_t fileSz, void *(*function)(void *), int idx0, int idx1) {
    int i;
    thread_param_t param[NTHREAD];
    pthread_t tid[NTHREAD];

    // 如果文件较小, 则不开启多线程
    if (fileSz < NTHREAD * MSIZE * BLKSZ) {
    // if(1) {
        param[0].start = 0;
        param[0].fileSz = fileSz;
        param[0].step = 1;
        param[0].idx0 = idx0;
        param[0].idx1 = idx1;
        function(&param[0]);
        return;
    }
    
    /*
    for (i = 0; i < NTHREAD; i++) {
        param[i].start = i * MSIZE * BLKSZ;
        param[i].fileSz = fileSz;
        param[i].step = NTHREAD;
        param[i].idx0 = idx0;
        param[i].idx1 = idx1;
        pthread_create(&tid[i], NULL, function, (void *)(&param[i]));
    }
    for (i = 0; i < NTHREAD; i++) {
        pthread_join(tid[i], NULL);
    }
    */
    off_t start;
    const off_t barrierLen = (1L << 31) - ((1L << 31) % (NTHREAD * MSIZE * BLKSZ)) + (NTHREAD * MSIZE * BLKSZ);
    for(start = 0; start < fileSz; start += barrierLen) {
        // printf("start: %ld\n", start);
        for (i = 0; i < NTHREAD; i++) {
            param[i].start = start + i * MSIZE * BLKSZ;
            param[i].fileSz = fileSz < start + barrierLen ? fileSz: start + barrierLen;
            param[i].step = NTHREAD;
            param[i].idx0 = idx0;
            param[i].idx1 = idx1;
            pthread_create(&tid[i], NULL, function, (void *)(&param[i]));
        }
        for (i = 0; i < NTHREAD; i++) {
            pthread_join(tid[i], NULL);
        }
    }
}

/// @brief 从源文件指定偏移处读取一个matrix, 计算并输出到disk的指定位置
/// @param buf BLKSZ大小的缓冲区
/// @param sbuf BLKSZ大小的缓冲区 用于存放对角线计算的S
/// @param matrix MSIZE * BLKSZ大小的缓冲区 用于存放读取的matrix
/// @param start 读取开始位置在源文件中的偏移
void write_one_matrix(char *buf, char *sbuf, char *matrix, off_t start) {
    int i, j, k;
    ssize_t blk_rd; // 读取的block数量
    off_t file_offset = start / NCOL; // 在单个文件(disk_*)中的偏移
    #define INDEX(row, col) ((row) * NCOL + (col))
    #define MDATA(row, col) matrix + INDEX(row, col) * BLKSZ

    // 先将一个矩阵读取进内存
    // 只会返回BLKSZ的整数倍
    blk_rd = fast_read(file_fd, matrix, MSIZE * BLKSZ, start) / BLKSZ;

    if (blk_rd == 0) {
        return;
    }

    // 将源数据写入磁盘
    for (i = 0; i < blk_rd; i++) {
        fast_write_check(data_fds[i % NCOL], matrix + i * BLKSZ, BLKSZ, file_offset + i / NCOL * BLKSZ);
    }

    // 计算奇偶校验并写入磁盘
    // 计算行校验
    for (i = 0; i < NROW; i++) {
        if (INDEX(i, 0) >= blk_rd) {
            break;
        }
        memcpy(buf, MDATA(i, 0), BLKSZ);
        for (j = 1; j < NCOL && INDEX(i, j) < blk_rd; j++) {
            xor_block(buf, MDATA(i, j));
        }
        fast_write_check(data_fds[NCOL], buf, BLKSZ, file_offset + i * BLKSZ);
    }

    // 计算对角校验
    // 计算S
    i = NROW - 1; j = 1;
    if (INDEX(i, j) < blk_rd) {
        memcpy(sbuf, MDATA(i, j), BLKSZ);
    } else {
        memset(sbuf, 0, BLKSZ);
    }
    i--; j++;
    for (; i >= 0; i--, j++) {
        if (INDEX(i, j) < blk_rd) {
            xor_block(sbuf, MDATA(i, j));
        }
    }

    // 计算实际的对角线奇偶校验
    for (k = 0; k < NROW; k++) {
        // 每一行奇偶校验
        memcpy(buf, sbuf, BLKSZ);
        for (i = 0, j = k; i < NROW; i++, j = (j - 1 + NCOL) % NCOL) {
            if (INDEX(i, j) < blk_rd) {
                // 这里不能将判断条件像之前那样放在for里面, 因为这里可以视为随机读写
                xor_block(buf, MDATA(i, j));
            }
        }
        fast_write_check(data_fds[NCOL+1], buf, BLKSZ, file_offset + k * BLKSZ);
    }
}

void* write_thread(void *thread_param) {
    off_t offset;
    char *buf, *sbuf, *matrix;
    thread_param_t *param = (thread_param_t *)thread_param;
    off_t start = param->start;
    off_t fileSz = param->fileSz;
    int step = param->step;

    buf = malloc(BLKSZ);
    sbuf = malloc(BLKSZ); // 用于存储对角线校验的S
    matrix = malloc(MSIZE * BLKSZ);
    for (offset = start; offset < fileSz; offset += step * MSIZE * BLKSZ) {
        write_one_matrix(buf, sbuf, matrix, offset);
    }
    free(buf);
    free(sbuf);
    free(matrix);
    return NULL;
}

void eo_write(char *input_path, char *filename) {
    int i, metafd;
    off_t fileSz;
    char path[PATH_LEN];
    
    // sem_init(&wFinish, 0, 0);

    // open files
    file_fd = Open(input_path, O_RDONLY);
    data_fds = malloc((NCOL + 2) * sizeof(int));
    for (i = 0; i < NCOL + 2; i++) {
        snprintf(path, PATH_LEN, "./disk_%d/%s", i, filename);
        data_fds[i] = OpenMode(path, O_CREAT | O_WRONLY | O_TRUNC, 0644);
    }

    fileSz = FileSize(file_fd);
    thread_create_and_join(fileSz, write_thread, -1, -1);

    // 将文件大小写入metadisk
    snprintf(path, PATH_LEN, "./metadisk/%s", filename);
    metafd = OpenMode(path, O_CREAT | O_WRONLY | O_TRUNC, 0644);
    // printf("fileSz: %ld\n", fileSz);
    fast_write_check(metafd, &fileSz, sizeof(off_t), 0);
    close(metafd);

    // while (atomic_load(&wCnt_atomic) > 0) {
    //     sem_wait(&wFinish);
    // }
    // sem_destroy(&wFinish);

    // close all fds
    close(file_fd);
    for (i = 0; i < NCOL + 2; i++) {
        close(data_fds[i]);
    }
}

/// @brief 一列非对角线校验出错, 支持非修复模式
void* eo_repair_single_nots(void *thread_param)
{
    thread_param_t *param = (thread_param_t *)thread_param;
    off_t start = param->start;
    off_t fileSz = param->fileSz;
    int step = param->step;
    int idx = param->idx0;

    off_t offset, file_offset;
    int valid_blk;
    int row_blk_rd, init = idx == NCOL ? 0: NCOL;
    int end_row_idx = NROW, buf_sz = NROW * BLKSZ;

    char *buf = malloc(NROW * BLKSZ), *n_buf = malloc(NROW * BLKSZ);
    char *matrix = malloc(MSIZE * BLKSZ);

    for (offset = start; offset < fileSz; offset += step * MSIZE * BLKSZ) {
        file_offset = offset / NCOL;
        // 这个矩阵中有多少有效块
        valid_blk = (fileSz - offset) / BLKSZ > MSIZE ? MSIZE: (fileSz - offset) / BLKSZ;
        // 对每个矩阵读取普通的未损坏数据，并与行异或结果异或

        end_row_idx = COL_VALID(valid_blk, 0);
        row_blk_rd = end_row_idx; // 这一列中可以读取多少块
        fast_read_check(data_fds[init], buf, row_blk_rd * BLKSZ, file_offset);
        if (end_row_idx != NROW)
        {
            memset(buf + end_row_idx * BLKSZ, 0, (NROW - end_row_idx) * BLKSZ);
        }
        for (int i = 0; init != NCOL && i < row_blk_rd; i++) {
            memcpy(matrix + (i * NCOL + init) * BLKSZ, buf + i * BLKSZ, BLKSZ);
        }

        for (int i = 0; i <= NCOL; i++)
        {
            if(i == idx || i == init)
                continue;
            if(i == NCOL)
                row_blk_rd = end_row_idx;
            else
                row_blk_rd = COL_VALID(valid_blk, i);
            // 最后一个矩阵的某行可能不完整，记录下该行没有数据的最左一列
            // 不可能是0，如果是0，代表这一行没有数据，这种情况下是不会读这一行的
            fast_read_check(data_fds[i], n_buf, row_blk_rd * BLKSZ, file_offset);
            for (int j = 0; i != NCOL && j < row_blk_rd; j++) {
                memcpy(matrix + (j * NCOL + i) * BLKSZ, n_buf + j * BLKSZ, BLKSZ);
            }
            for(int j = 0; j < row_blk_rd; j++)
            {
                // 将读取到的数据和buf中对应的位置异或
                xor_block(buf + j * BLKSZ, n_buf + j * BLKSZ);
            }
        }

        if (file_fd != -1) {
            // 写入文件
            row_blk_rd = COL_VALID(valid_blk, idx);
            for (int i = 0; i < row_blk_rd; i++) {
                memcpy(matrix + (i * NCOL + idx) * BLKSZ, buf + i * BLKSZ, BLKSZ);
            }
            fast_write_check(file_fd, matrix, valid_blk * BLKSZ, offset);
            continue;
        }

        printf("repair\n");
        // 将结果写入文件
        if(valid_blk != MSIZE)
        {
            if(idx != NCOL)
                buf_sz = (COL_VALID(valid_blk, idx)) * BLKSZ;
            else
                buf_sz = end_row_idx * BLKSZ;
        }
        fast_write_check(data_fds[idx], buf, buf_sz, file_offset);
    }
    free(buf);
    free(n_buf);
    return NULL;
}

void* eo_repair_single_s(void *thread_param)
{
    thread_param_t *param = (thread_param_t *)thread_param;
    off_t start = param->start;
    off_t fileSz = param->fileSz;
    int step = param->step;

    off_t offset, file_offset;
    int valid_blk;
    int n_offset, row_blk_rd, s_init, end_row_idx;
    char *buf = malloc(NROW * BLKSZ);
    char *n_buf = malloc(NROW * BLKSZ);
    char *s_buf = malloc(BLKSZ);
    
    for (offset = start; offset < fileSz; offset += step * MSIZE * BLKSZ) {
        file_offset = offset / NCOL;
        s_init = 0;
        // 对每个矩阵读取普通数据，并计算对角线异或结果

        // 这个矩阵中有多少有效块
        valid_blk = (fileSz - offset) / BLKSZ > MSIZE ? MSIZE: (fileSz - offset) / BLKSZ;

        // 最后一个矩阵可能不完整，记录下第一列中没有数据的最上面一行
        end_row_idx = COL_VALID(valid_blk, 0);
        row_blk_rd = end_row_idx;
        fast_read_check(data_fds[0], buf, row_blk_rd * BLKSZ, file_offset);
        // 如果数据有残缺, 则将残缺的部分填充为0
        if (end_row_idx != NROW)
        {
            memset(buf + end_row_idx * BLKSZ, 0, (NROW - end_row_idx) * BLKSZ);
        }
        for (int i = 1; i < NCOL; i++)
        {
            row_blk_rd = COL_VALID(valid_blk, i);
            // 最后一个矩阵的某行可能不完整，记录下该行已没有数据
            fast_read_check(data_fds[i], n_buf, row_blk_rd * BLKSZ, file_offset);
            for(int j = 0; j < row_blk_rd; j++)
            {
                // 列中最后一行数据块不存在时跳过
                int s_idx = (i + j) % p;
                n_offset = j * BLKSZ;
                if(s_idx == p - 1)
                {
                    if(!s_init)
                    {
                        s_init = 1;
                        memcpy(s_buf, n_buf + n_offset, BLKSZ);
                    }
                    else
                        xor_block(s_buf, n_buf + n_offset);
                }
                else
                {
                    xor_block(buf + s_idx * BLKSZ, n_buf + n_offset);
                }
            }
        }
        
        // 和S异或
        for (int i = 0; i < NROW; i++)
        {
            xor_block(buf + i * BLKSZ, s_buf);
        }
        fast_write_check(data_fds[NCOL + 1], buf, NROW * BLKSZ, file_offset);
    }
    free(buf);
    free(n_buf);
    free(s_buf);
    return NULL;
}

/// @brief 一列数据一列对角校验出错, 不支持非修复模式, 请使用eo_repair_single_nots
void* eo_repair_double_s(void *thread_param)
{
    thread_param_t *param = (thread_param_t *)thread_param;
    off_t start = param->start;
    off_t fileSz = param->fileSz;
    int step = param->step;
    int idx = param->idx0;

    off_t offset, file_offset;
    int valid_blk;
    int buf_offset, init = idx != 0 ? 0: 1;
    int end_row_idx, col_sz, i, j, k;
    const int buf_sz = NROW * BLKSZ, idx_offset = idx * NROW * BLKSZ, s_offset = (NCOL + 1) * NROW * BLKSZ;

    char *buf = malloc(REAL_MSIZE * BLKSZ);
    char *s_buf = malloc(BLKSZ);

    for (offset = start; offset < fileSz; offset += step * MSIZE * BLKSZ) {
        file_offset = offset / NCOL;
        // 这个矩阵中有多少有效块
        valid_blk = (fileSz - offset) / BLKSZ > MSIZE ? MSIZE: (fileSz - offset) / BLKSZ;
        // 最后一个矩阵可能不完整，记录下第一列中没有数据的最上面一行，最后一行中没有数据的最下面一列
        for (i = 0; i < NCOL + 1; i++)
        {
            if(i == idx)
                continue;
            if (i != NCOL)
                end_row_idx = COL_VALID(valid_blk, i);
            else
                end_row_idx = COL_VALID(valid_blk, 0);
            
            fast_read_check(data_fds[i], buf + i * NROW * BLKSZ, end_row_idx * BLKSZ, file_offset);
        }
        // 修复非对角线异或列
        col_sz = (COL_VALID(valid_blk, init)) * BLKSZ;
        memcpy(buf + idx_offset, buf + init * NROW * BLKSZ, col_sz);
        memset(buf + idx_offset + col_sz, 0, buf_sz - col_sz);

        if (idx == NCOL)
            col_sz = (COL_VALID(valid_blk, 0)) * BLKSZ;
        else
            col_sz = (COL_VALID(valid_blk, idx)) * BLKSZ; // 缺失的那一列有多少块
        for(j = init + 1; j <= NCOL; j++)
        {
            if(j == idx)
                continue;
            for(i = 0; i < col_sz / BLKSZ; i++)
            {
                // 将读取到的数据和buf中对应的位置异或
                if (i * NCOL + j < valid_blk || j == NCOL)
                    // 因为for循环中使用的是col_sz, 故这里在j == NCOL不用额外判断行数的合法性
                    xor_block(buf + idx_offset + i * BLKSZ, buf + (i + j * NROW) * BLKSZ);
            }
        }
        // 计算对角校验
        // 计算S
        i = NROW - 1; j = 1;
        if (i * NCOL + j < valid_blk)
        {
            memcpy(s_buf, buf + (i + j * NROW) * BLKSZ, BLKSZ);
        }
        else
        {
            memset(s_buf, 0, BLKSZ);
        }
        i--; j++;
        for (; i >= 0; i--, j++)
        {
            if (i * NCOL + j < valid_blk)
            {
                xor_block(s_buf, buf + (i + j * NROW) * BLKSZ);
            }
        }

        // 计算实际的对角线奇偶校验
        for (k = 0; k < NROW; k++)
        {
            buf_offset = s_offset + k * BLKSZ;
            // 每一行奇偶校验
            memcpy(buf + buf_offset, s_buf, BLKSZ);
            for (i = 0, j = k; i < NROW; i++, j = (j - 1 + NCOL) % NCOL)
            {
                if (i * NCOL + j < valid_blk)
                {
                    xor_block(buf + buf_offset, buf + (i + j * NROW) * BLKSZ);
                }
            }
        }
        fast_write_check(data_fds[idx], buf + idx_offset, col_sz, file_offset);
        fast_write_check(data_fds[NCOL + 1], buf + s_offset, buf_sz, file_offset);
    }
    free(buf);
    free(s_buf);
    return NULL;
}

/// @brief 一列数据一列行校验出错, 支持非修复模式
void* eo_repair_double_n(void *thread_param)
{
    thread_param_t *param = (thread_param_t *)thread_param;
    off_t start = param->start;
    off_t fileSz = param->fileSz;
    int step = param->step;
    int idx = param->idx0;

    off_t offset, file_offset;
    int buf_offset, n_offset, valid_blk, max_row;
    int end_row_idx, col_sz, i, j, k;
    const int idx_offset = idx * NROW * BLKSZ, nn_offset = NCOL * NROW * BLKSZ;

    char *buf = malloc(REAL_MSIZE * BLKSZ);
    char *s_buf = malloc(BLKSZ);

    for (offset = start; offset < fileSz; offset += step * MSIZE * BLKSZ) {
        file_offset = offset / NCOL;
        valid_blk = (fileSz - offset) / BLKSZ > MSIZE ? MSIZE: (fileSz - offset) / BLKSZ;
        max_row = COL_VALID(valid_blk, 0);
        // 最后一个矩阵可能不完整，记录下第一列中没有数据的最上面一行，最后一行中没有数据的最下面一列
        for (i = 0; i <= NCOL + 1; i++)
        {
            if(i == idx || i == NCOL)
                continue;
            n_offset = i * NROW * BLKSZ;
            if (i == NCOL)
                end_row_idx = max_row;
            else if (i == NCOL + 1)
                end_row_idx = NROW;
            else
                end_row_idx = COL_VALID(valid_blk, i);

            fast_read_check(data_fds[i], buf + n_offset, end_row_idx * BLKSZ, file_offset);
        }

        // 通过对角校验恢复一列普通数据
        // 计算S
        i = (idx + NCOL - 1) % NCOL;
        j = NCOL + 1;
        if (i < NROW)
            memcpy(s_buf, buf + (i + j * NROW) * BLKSZ, BLKSZ);
        else
            memset(s_buf, 0, BLKSZ);
        for(j = 0; j < NCOL; j++, i = (idx - j - 1 + NCOL) % NCOL)
        {
            if (NCOL * i + j < valid_blk)
            {
                n_offset = (i + j * NROW) * BLKSZ;
                xor_block(s_buf, buf + n_offset);
            }
        }

        // 每一个对角线可以计算出坏的一个块
        for (k = 0; k < NROW; k++)
        {
            // 每一行奇偶校验
            buf_offset = idx_offset + k * BLKSZ;
            memcpy(buf + buf_offset, s_buf, BLKSZ);
            i = (idx + k) % p;
            j = NCOL + 1;
            if (i < NROW)
            {
                n_offset = (i + j * NROW) * BLKSZ;
                xor_block(buf + buf_offset, buf + n_offset);
            }
            for (j = 0; j < NCOL; j++, i = (idx + k - j + NCOL) % NCOL)
            {
                if(j == idx)
                    continue;
                if (NCOL * i + j < valid_blk)
                {
                    n_offset = (i + j * NROW) * BLKSZ;
                    xor_block(buf + buf_offset, buf + n_offset);
                }
            }
        }

        if (file_fd != -1) {
            // 如果只是想读, 那么就不用修行异或列
            // 写入文件
            for (i = 0; i < NROW; i++) {
                for (j = 0; j < NCOL && NCOL * i + j < valid_blk; j++) {
                    n_offset = (i + j * NROW) * BLKSZ;
                    fast_write_check(file_fd, buf + n_offset, BLKSZ, offset + (NCOL * i + j) * BLKSZ);
                }
            }
            continue;
        }

        // 计算行异或列
        memcpy(buf + nn_offset, buf, max_row * BLKSZ);
        if(max_row != NROW)
            memset(buf + nn_offset + max_row * BLKSZ, 0, (NROW - max_row) * BLKSZ);
        for(i = 1; i < NCOL; i++)
        {
            n_offset = i * NROW * BLKSZ;
            for(j = 0; j < NROW; j++)
            {
                if (NCOL * j + i < valid_blk)
                {
                    // 将读取到的数据和buf中对应的位置异或
                    xor_block(buf + nn_offset + j * BLKSZ, buf + n_offset + j * BLKSZ);
                }
            }
        }

        col_sz = (COL_VALID(valid_blk, idx)) * BLKSZ; // 缺失的那一列有多少块
        fast_write_check(data_fds[idx], buf + idx_offset, col_sz, file_offset);
        fast_write_check(data_fds[NCOL], buf + nn_offset, max_row * BLKSZ, file_offset);
    }
    free(buf);
    free(s_buf);
    return NULL;
}

/// @brief 两列数据列出错, 支持非修复模式
void* eo_repair_double_2n(void *thread_param)
{
    thread_param_t *param = (thread_param_t *)thread_param;
    off_t start = param->start;
    off_t fileSz = param->fileSz;
    int step = param->step;
    int idx0 = param->idx0;
    int idx1 = param->idx1;
    
    off_t offset, file_offset;
    int buf_offset, n_offset, valid_blk, max_row = 0;
    int end_row_idx, col_sz0, col_sz1, i, j, k;
    const int idx0_offset = idx0 * NROW * BLKSZ, idx1_offset = idx1 * NROW * BLKSZ;
    const int nn_offset = NCOL * NROW * BLKSZ, s_offset = (NCOL + 1) * NROW * BLKSZ, s1_offset = (NROW + 1) * BLKSZ;

    char *buf = malloc(REAL_MSIZE * BLKSZ);
    char *s_buf = malloc(BLKSZ);
    char *n_buf = malloc(2 * (NROW + 1) * BLKSZ);

    char path[PATH_LEN];
    struct timeval time;
    const off_t barrierLen = (1L << 31) - ((1L << 31) % (NTHREAD * MSIZE * BLKSZ)) + (NTHREAD * MSIZE * BLKSZ);
    int record;
    if (start < barrierLen) {
        snprintf(path, PATH_LEN, "./record%ld.txt", start / BLKSZ / MSIZE);
        record = OpenMode(path, O_CREAT | O_WRONLY | O_TRUNC, 0644);
    } else {
        snprintf(path, PATH_LEN, "./record%ld.txt", (start % barrierLen) / BLKSZ / MSIZE);
        record = Open(path, O_WRONLY | O_APPEND);
    }

    for (offset = start; offset < fileSz; offset += step * MSIZE * BLKSZ) {
        gettimeofday(&time, NULL);
        snprintf(path, PATH_LEN, "%ld\n", time.tv_sec * 1000000 + time.tv_usec);
        write(record, path, strlen(path));
        file_offset = offset / NCOL;
        valid_blk = (fileSz - offset) / BLKSZ > MSIZE ? MSIZE: (fileSz - offset) / BLKSZ;
        max_row = COL_VALID(valid_blk, 0);
        // 最后一个矩阵可能不完整，记录下第一列中没有数据的最上面一行，最后一行中没有数据的最下面一列
        for (i = 0; i <= NCOL + 1; i++)
        {
            if(i == idx0 || i == idx1)
                continue;
            n_offset = i * NROW * BLKSZ;

            if (i == NCOL)
                end_row_idx = max_row;
            else if (i < NCOL)
                end_row_idx = COL_VALID(valid_blk, i);
            else
                end_row_idx = NROW;

            fast_read_check(data_fds[i], buf + n_offset, end_row_idx * BLKSZ, file_offset);
        }

        // 计算S
        memset(s_buf, 0, BLKSZ);
        for(i = 0; i < NROW; i++)
        {
            if (i < max_row)
            {
                n_offset = nn_offset + i * BLKSZ;
                xor_block(s_buf, buf + n_offset);
            }
            n_offset = s_offset + i * BLKSZ;
            xor_block(s_buf, buf + n_offset);
        }

        // 计算辅助数组S0
        for (i = 0; i < max_row; i++)
        {
            n_offset = i * BLKSZ;
            memcpy(n_buf + n_offset, buf + nn_offset + n_offset, BLKSZ);
            for(j = 0; j < NCOL; j++)
            {
                if(j == idx0 || j == idx1)
                    continue;
                if(i * NCOL + j < valid_blk)
                {
                    xor_block(n_buf + n_offset, buf + j * NROW * BLKSZ + n_offset);
                }
            }
        }
        memset(n_buf + max_row * BLKSZ, 0, (NROW - max_row + 1) * BLKSZ);

        // 计算辅助数组S1
        for (i = 0; i <= NROW; i++)
        {
            n_offset = s1_offset + i * BLKSZ;
            memcpy(n_buf + n_offset, s_buf, BLKSZ);
            if(i != NROW)
                xor_block(n_buf + n_offset, buf + s_offset + i * BLKSZ);
        }
        for (k = 0; k <= NROW; k++)
        {
            n_offset = s1_offset + k * BLKSZ;
            for(j = 0; j < NCOL; j++)
            {
                if(j == idx0 || j == idx1)
                    continue;
                i = (k - j + NCOL) % NCOL;
                if(i * NCOL + j < valid_blk)
                {
                    xor_block(n_buf + n_offset, buf + (j * NROW + i) * BLKSZ);
                }
            }
        }

        //循环计算丢失的数据列
        i = (NCOL - (idx1 - idx0) - 1) % NCOL;
        do
        {
            if(i != NROW)
            {
                // 计算a_s,idx1
                n_offset = idx1_offset + i * BLKSZ;
                j = (idx1 + i) % NCOL;
                memcpy(buf + n_offset, n_buf + s1_offset + j * BLKSZ, BLKSZ);
                k = (i + idx1 - idx0) % NCOL;
                if(k != NROW)
                    xor_block(buf + n_offset, buf + idx0_offset + k * BLKSZ);
                // 计算a_s,idx0
                buf_offset = idx0_offset + i * BLKSZ;
                memcpy(buf + buf_offset, n_buf + i * BLKSZ, BLKSZ);
                xor_block(buf + buf_offset, buf + n_offset);
            }
            i = (NCOL + i - (idx1 - idx0)) % NCOL;
        } while(i != NCOL - 1);

        // 缺失的那两列有多少块
        if (file_fd != -1) {
            // 写入文件
            for (i = 0; i < NROW; i++) {
                for (j = 0; j < NCOL && NCOL * i + j < valid_blk; j++) {
                    n_offset = (i + j * NROW) * BLKSZ;
                    fast_write_check(file_fd, buf + n_offset, BLKSZ, offset + (NCOL * i + j) * BLKSZ);
                }
            }
        } else {
            // 修复磁盘
            col_sz0 = (COL_VALID(valid_blk, idx0)) * BLKSZ;
            col_sz1 = (COL_VALID(valid_blk, idx1)) * BLKSZ;
            fast_write_check(data_fds[idx0], buf + idx0_offset, col_sz0, file_offset);
            fast_write_check(data_fds[idx1], buf + idx1_offset, col_sz1, file_offset);
        }
    }
    free(buf);
    free(s_buf);
    free(n_buf);
    close(record);
    return NULL;
}

off_t repair_open(int idx0, int idx1, char *filename)
{
    char path[PATH_LEN];
    int metafd;
    off_t fileSz;
    
    snprintf(path, PATH_LEN, "./metadisk/%s", filename);
    metafd = Open(path, O_RDONLY);
    fast_read_check(metafd, &fileSz, sizeof(off_t), 0);
    close(metafd);

    for (int i = 0; i <= NCOL + 1; i++)
    {
        snprintf(path, PATH_LEN, "./disk_%d/%s", i, filename);
        if((idx0 != -1 && i == idx0) || (idx1 != -1 && i == idx1))
            data_fds[i] = OpenMode(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
        else
            data_fds[i] = Open(path, O_RDONLY);
    }
    return fileSz;
}

void repair_close()
{
    for (int i = 0; i <= NCOL + 1; i++)
    {
        close(data_fds[i]);
    }
}

void eo_repair_one_file(int idx0, int idx1, off_t fileSz) {
    int tmp_idx;
    // 只有1列出错
    if(idx1 < 0)
    {
        // 非对角校验列出错
        if(idx0 <= p)
        {
            thread_create_and_join(fileSz, eo_repair_single_nots, idx0, idx1);
        }
        // 对角校验列出错
        else if (idx0 == p + 1)
        {
            thread_create_and_join(fileSz, eo_repair_single_s, idx0, idx1);
        }
        else
            printf("idx(%d) error\n", idx0);
    }
    // 有2列出错
    else if(idx0 >= 0)
    {
        // 调整编号顺序
        if(idx0 > idx1)
        {
            tmp_idx = idx0;
            idx0 = idx1;
            idx1 = tmp_idx;
        }
        // 对角校验列出错
        if(idx1 == p + 1)
        {
            if (file_fd != -1)
                thread_create_and_join(fileSz, eo_repair_single_nots, idx0, idx1);
            else
                thread_create_and_join(fileSz, eo_repair_double_s, idx0, idx1);
        }
        // 行校验列和一列普通数据列出错
        else if(idx1 == p)
        {
            thread_create_and_join(fileSz, eo_repair_double_n, idx0, idx1);
        }
        // 两列普通数据列出错
        else if(idx1 < p)
        {
            thread_create_and_join(fileSz, eo_repair_double_2n, idx0, idx1);
        }
        else
            printf("idx(%d, %d) error\n", idx0, idx1);
    }
}

void eo_repair(int idx0, int idx1) {
    char path[PATH_LEN];
    int tmp_idx;
    DIR * dir;
    struct dirent * dirp;
    off_t fileSz;
    // 寻找一个完好的目录读取文件
    for(tmp_idx = p + 1; tmp_idx >= 0; tmp_idx--)
        if(tmp_idx != idx0 && tmp_idx != idx1)
            break;
    snprintf(path, PATH_LEN, "./disk_%d/", tmp_idx);
    dir = opendir(path);
    if(dir == NULL) {
        printf("open dir %s error\n", path);
        return;
    }
    // 分配存储和计算的空间
    data_fds = malloc((NCOL + 2) * sizeof(int));
    // 读取目录下所有文件，依次修复
    while (1)
    {
        dirp = readdir(dir);
        if(dirp == NULL)
            break;
        else if(dirp->d_type == DT_REG) {
            file_fd = -1; // 提示不要写入文件, 而是写入磁盘
            fileSz = repair_open(idx0, idx1, dirp->d_name);
            eo_repair_one_file(idx0, idx1, fileSz);
            repair_close();
        }
    }

    free(data_fds);
    closedir(dir);
}

void* read_thread(void *thread_param) {
    thread_param_t *param = (thread_param_t *)thread_param;
    off_t start = param->start;
    off_t fileSz = param->fileSz;
    int step = param->step;

    off_t offset, file_offset;
    int valid_blk, end_row_idx, i, j;

    char *buf = malloc(MSIZE * BLKSZ);
    char *nbuf = malloc(NROW * BLKSZ);

    for (offset = start; offset < fileSz; offset += step * MSIZE * BLKSZ) {
        file_offset = offset / NCOL;
        valid_blk = (fileSz - offset) / BLKSZ > MSIZE ? MSIZE: (fileSz - offset) / BLKSZ;
        for (j = 0; j < NCOL; j++) {
            end_row_idx = COL_VALID(valid_blk, j);
            fast_read_check(data_fds[j], nbuf, end_row_idx * BLKSZ, file_offset);
            for (i = 0; i < end_row_idx; i++) {
                memcpy(buf + (i * NCOL + j) * BLKSZ, nbuf + i * BLKSZ, BLKSZ);
            }
        }
        fast_write_check(file_fd, buf, valid_blk * BLKSZ, offset);
    }
    free(buf);
    free(nbuf);
    return NULL;
}

void eo_read(char *filename, char *output_path) {
    int i;
    off_t fileSz;
    char path[PATH_LEN];
    
    int bad_disk[MAX_BAD_DISK] = {-1, -1}, bad_disk_count = 0;

    for (i = 0; i < NCOL + 2; i++) {
        snprintf(path, PATH_LEN, "./disk_%d/%s", i, filename);
        if(access(path, F_OK) < 0)
        {
            if(bad_disk_count >= MAX_BAD_DISK)
            {
                printf("More than 2 disks lost\n");
                return;
            }
            bad_disk[bad_disk_count++] = i;
        }
    }
    // 提前打开file_fd, 在repair时, 如果file_fd不为-1则直接写到file_fd中, 而不是修复
    file_fd = OpenMode(output_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    data_fds = malloc((NCOL + 2) * sizeof(int));
    fileSz = repair_open(bad_disk[0], bad_disk[1], filename);
    if (bad_disk_count == 0 || (bad_disk_count == 1 && bad_disk[0] >= NCOL) || (bad_disk[0] >= NCOL && bad_disk[1] >= NCOL) ) {
        // 无损坏或者只有校验列损坏, 直接读
        thread_create_and_join(fileSz, read_thread, -1, -1);
    } else {
        eo_repair_one_file(bad_disk[0], bad_disk[1], fileSz);
    }
    repair_close();
    free(data_fds);
    close(file_fd);
}

int main(int argc, char *argv[]) {
    if (unlikely(argc < 4)) {
        perror("Invalid number of arguments");
        return -1;
    }
    
    p = atoi(argv[2]);
    check_all_disk(p + 2);

    if (!memcmp(argv[1], "read", 4)) {
        eo_read(argv[3], argv[4]);
    } else if (!memcmp(argv[1], "write", 5)) {
        eo_write(argv[3], argv[4]);
    } else if (!memcmp(argv[1], "repair", 6)) {
        if (argc == 4) {
            eo_repair(atoi(argv[3]), -1);
        } else if (argc == 5) {
            eo_repair(atoi(argv[3]), atoi(argv[4]));
        } else {
            perror("Invalid number of arguments");
            return -1;
        }
    } else {
        perror("Invalid argument1");
        return -1;
    }

    return 0;
}
